/**
 * calculator
 *
 * @author : tzy
 * @Date : 2021-03-16
 * @Description:
 **/

package com.tang.calculator;

import com.tang.calculator.config.Formula;
import com.tang.calculator.config.FormulaConfigVO;
import com.tang.calculator.context.CalculateContext;
import com.tang.calculator.symbols.OperatorSymbol;
import com.tang.calculator.symbols.ReferenceValueSymbol;
import com.tang.calculator.symbols.Symbol;
import com.tang.calculator.symbols.ValueSymbol;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

@Slf4j
@Component("defaultCalculator")
public class DefaultCalculator implements Calculator {

    @Autowired
    FormulaSpliter formulaSpliter;
    @Autowired
    CheckFormula checkFormula;

    @Override
    public BigDecimal execute(Formula formula) {
        //构建上下文
        CalculateContext context = createContext(formula);

        //检查公式
        this.checkFormula(formula.getFormula());

        //分割公式
        formulaSpliter.split(context);

        setContext(context);

        //执行公式
        return doExecute(context);
    }

    public BigDecimal execute(CalculateContext context) {

        //检查公式
        this.checkFormula(context.getFormula());

        System.out.println(checkFormula.check(context));
        context.getSymbolList().clear();
        //分割公式
        formulaSpliter.split(context);

        setContext(context);

        //执行公式
        return doExecute(context);
    }

    private void setContext(CalculateContext context) {
        List<Symbol> symbolList = context.getSymbolList();
        for (Symbol symbol : symbolList) {
            if(symbol instanceof ReferenceValueSymbol) {
                ReferenceValueSymbol valueSymbol = (ReferenceValueSymbol) symbol;
                valueSymbol.setContext(context);
            }
        }
    }

    private BigDecimal doExecute(CalculateContext context) {
        List<Symbol> symbolList = context.getSymbolList();
        Iterator<Symbol> iterator = symbolList.iterator();

        for (int i = 0; i < symbolList.size(); i++) {
            Symbol symbol = symbolList.get(i);
            if (symbol instanceof ValueSymbol) {
                context.getValueStack().push((ValueSymbol) symbol);
            } else {
                OperatorSymbol operatorSymbol = (OperatorSymbol) symbol;
                operatorSymbol.operate(context);
            }
        }

        while (isExecuteAble(context)) {
            doExecuteOnce(context);
        }
        Stack<ValueSymbol> valueStack = context.getValueStack();
        ValueSymbol result = valueStack.pop();
        return result.getValue();
    }

    private void doExecuteOnce(CalculateContext context) {
        OperatorSymbol operatorSymbol = context.getOperatorStack().pop();
        operatorSymbol.simpleOperate(context);
    }

    private boolean isExecuteAble(CalculateContext context) {
        return !context.getOperatorStack().isEmpty();
    }


    protected CalculateContext createContext(Formula formula) {
        return new CalculateContext(formula);
    }

    private void checkFormula(String formula) {

    }

    protected void executeResultCheck() {

    }

}
